﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime;

namespace Collections
{
    [Serializable]
    public class FMSet<T> : ISet<T>
    {
        #region Properties
        /// <summary>
        /// 
        /// </summary>
        public int Count
        {
            get { return this._size; }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                {
                    throw new IndexOutOfRangeException();
                }
                return this._array[index];
            }
        }
        public int Capacity
        {
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
            get { return this._array.Length; }
            set
            {
                if (value < this._size)
                {
                    throw new ArgumentOutOfRangeException();
                }
                if (value != this._array.Length)
                {
                    if (value > 0)
                    {
                        T[] destinationArray = new T[value];
                        if (this._size > 0)
                        {
                            Array.Copy(_array, 0, destinationArray, 0, this._size);
                        }
                        _array = destinationArray;
                    }
                    else
                    {
                        this._array = FMSet<T>._emptyArray;
                    }
                }
            }
        }
        #endregion
        #region Fields
        private const int _defaultCapacity = 4;
        private T[] _array;
        private int _size;
        private readonly static T[] _emptyArray;
        #endregion
        #region Constructors
        /// <summary>
        /// 
        /// </summary>
        static FMSet()
        {
            _emptyArray = new T[0];
        }
        /// <summary>
        /// 
        /// </summary>
        public FMSet()
        {
            _array = FMSet<T>._emptyArray;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="collection"></param>
        public FMSet(IEnumerable<T> collection)
        {
            if (collection == null)
            {
                throw new ArgumentNullException();
            }
            this._size = 0;
            this._array = new T[_defaultCapacity];
            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    this.Add(enumerator.Current);
                }
            }

        }
        #endregion
        #region Private Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private void ensureCapacity(int min)
        {
            if (this._array.Length < min)
            {
                int num = (this._array.Length == 0) ? _defaultCapacity : (this._array.Length * 2);
                if (num < min)
                {
                    num = min;
                }
                Capacity = num;
            }
        }
        #endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Add(T item)
        {
            if (Contains(item))
            {
                return true;
            }
            if (this._size == this._array.Length)
            {
                this.ensureCapacity(this._size + 1);
            }
            this._array[_size++] = item;
            return true;
        }
        /// <summary>
        /// Current - other
        /// </summary>
        /// <param name="other"></param>
        public void ExceptWith(IEnumerable<T> other)
        {
            if (ReferenceEquals(this, other))
            {
                this.Clear();
                return;
            }
            using (IEnumerator<T> enumerator = other.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    this.Remove(enumerator.Current);
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public void IntersectWith(IEnumerable<T> other)
        {
            if (ReferenceEquals(this, other))
            {
                return;
            }
            List<T> tmpArray = new List<T>();
            using (IEnumerator<T> e = other.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (this.Contains(e.Current))
                    {
                        tmpArray.Add(e.Current);
                    }
                }
            }
            this.Clear();
            this.UnionWith(tmpArray);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public bool IsProperSupersetOf(IEnumerable<T> other)
        {
            if (IsSupersetOf(other))
            {
                FMSet<T> set = (FMSet<T>)other;
                if (set.Count == this.Count)
                {
                    return false;
                }
                return true;
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public bool IsProperSubsetOf(IEnumerable<T> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException();
            }
            FMSet<T> set = other as FMSet<T>;
            if (set == null)
            {
                throw new InvalidCastException();
            }
            return set.IsProperSupersetOf(this);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public bool IsSupersetOf(IEnumerable<T> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException();
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            FMSet<T> set = other as FMSet<T>;
            if (set == null)
            {
                throw new InvalidOperationException(string.Format("Type must be FMSet<{0}>"
                    , typeof(T).Name));
            }
            if (set.Count > this.Count)
            {
                return false;
            }
            foreach (T item in set)
            {
                if (!this.Contains(item))
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public bool IsSubsetOf(IEnumerable<T> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException();
            }
            FMSet<T> set = other as FMSet<T>;
            if (set == null)
            {
                throw new InvalidOperationException();
            }
            return set.IsSupersetOf(this);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public bool Overlaps(IEnumerable<T> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException();
            }
            using (IEnumerator<T> e = other.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (this.Contains(e.Current))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidCastException"></exception>
        public bool SetEquals(IEnumerable<T> other)
        {
            if (this.IsSubsetOf(other))
            {
                FMSet<T> set = (FMSet<T>)other;
                if (set.Count == this.Count)
                {
                    return true;
                }
                return false;
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool SetEqualsReference(FMSet<T> other)
        {
            foreach (T t in other)
            {
                if (this.ContainsReference(t))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            FMSet<T> set = obj as FMSet<T>;
            if (set == null)
            {
                return false;
            }
            return SetEquals(set);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        /// <summary>
        /// Not Implement
        /// </summary>
        /// <param name="other"></param>
        public void SymmetricExceptWith(IEnumerable<T> other)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public void UnionWith(IEnumerable<T> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException();
            }
            if (ReferenceEquals(this, other))
            {
                return;
            }
            using (IEnumerator<T> e = other.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    this.Add(e.Current);
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        void ICollection<T>.Add(T item)
        {
            this.Add(item);
        }
        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            if (this._size > 0)
            {
                Array.Clear(this._array, 0, _size);
                this._size = 0;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool ContainsReference(T item)
        {
            foreach (T i in _array)
            {
                if (ReferenceEquals(item, i))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Contain Items
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(T item)
        {
            foreach (T i in _array)
            {
                if (item.Equals(i))
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Not Implement
        /// </summary>
        /// <param name="array"></param>
        /// <param name="arrayIndex"></param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            for (int i = 0; i < this.Count; ++i)
            {
                array[arrayIndex + i] = _array[i];
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public bool Remove(T item)
        {
            int index = IndexOf(item);
            if (index >= 0)
            {
                this.RemoveAt(index);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public void RemoveAt(int index)
        {
            if (index >= this._size)
            {
                throw new ArgumentOutOfRangeException();
            }
            this._size--;
            if (index < this._size)
            {
                Array.Copy(this._array, index + 1, this._array, index, this._size - index);
            }
            this._array[this._size] = default(T);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public int IndexOf(T item)
        {
            return Array.IndexOf<T>(_array, item, 0, this._size);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new Enumerator<T>((FMSet<T>)this);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return new Enumerator<T>((FMSet<T>)this);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder strBuilder = new StringBuilder();
            foreach (T t in this)
            {
                strBuilder.AppendFormat("{0},", t);
            }
            return strBuilder.ToString();
        }
    }
    [Serializable, StructLayout(LayoutKind.Sequential)]
    internal class Enumerator<T> : IEnumerator<T>
    {
        #region Properties
        public T Current
        {
            get { return _current; }
        }
        object IEnumerator.Current
        {
            get { return _current; }
        }
        #endregion
        #region Fields
        private FMSet<T> _set;
        private T _current;
        private int _index;
        #endregion
        #region Constructor
        /// <summary>
        /// 
        /// </summary>
        /// <param name="set"></param>
        internal Enumerator(FMSet<T> set)
        {
            this._set = set;
            this._current = default(T);
            this._index = 0;
        }
        #endregion
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool MoveNext()
        {
            if (this._set.Count == 0
                || _index >= this._set.Count)
            {
                this._current = default(T);
                return false;
            }
            this._current = _set[_index++];
            return true;
        }
        /// <summary>
        /// 
        /// </summary>
        public void Reset()
        {
            this._current = default(T);
            this._index = 0;
        }
    }
}
